#!/usr/bin/python

import os
import sys
import datetime
import urllib
import re
from optparse import OptionParser

from yahoo.search.web import WebSearch
import imdb
import yahoo.search
from movie.vote.models import Movie


class ReviewFinder(object):
  # Used to avoid requerying IMDB in every subclass
  IMDB_CACHE = {}

  def __init__(self, movie_name, imdb_url=None, websearch_query=None):
    self.movie_name = movie_name
    self.imdb_url = imdb_url
    self.websearch_query = websearch_query

    if imdb_url:
      if imdb_url not in ReviewFinder.IMDB_CACHE:
        if not imdb_url.endswith('/'):
          print 'WARNING: IMDB URL for %s does not end with /' % (movie_name,)
          imdb_url += '/'
        url = imdb_url + 'externalreviews'
        ReviewFinder.IMDB_CACHE[imdb_url] = urllib.urlopen(url).readlines()
      self.imdb_extreviews_data = ReviewFinder.IMDB_CACHE[imdb_url]

  def _FindReviewUrl(self, url_data, html=True):
    """Should be implemented by subclasses."""
    return None

  def _FindRegex(self, regex, lines):
    c = re.compile(regex)
    found = []
    for line in lines:
      found.extend(c.findall(line))
    return found

  def _SearchWebForReview(self):
    if not self.websearch_query:
      return None
    srch = WebSearch(app_id='YahooDemo')
    srch.query = '%s %s' % (self.movie_name, self.websearch_query)
    srch.results = 1
    try:
      review_url = self._FindReviewUrl([x.Url for x in srch.parse_results()], html=False)
    except yahoo.search.SearchError:
      review_url = None
    if review_url:
      # Try to guess whether the search had good results by looking for title in url
      guesses = []
      title = self.movie_name.lower().replace(' ', '_')
      guesses.append(title)
      guesses.append(title.replace('_', ''))
      for guess in guesses:
        if len(guess) > 5 and guess in review_url:
          return review_url
      try:
        if raw_input('%s\n%s\nAdd from websearch?\n' % (self.movie_name, review_url)).startswith('y'):
          return review_url
      except EOFError:
        pass
    return None

  def GetReviewUrl(self, search_web):
    review_url = self._FindReviewUrl(self.imdb_extreviews_data)
    if review_url:
      return review_url
    if search_web:
      review_url = self._SearchWebForReview()
      if review_url:
        return review_url
    return None

class MetacriticReviewFinder(ReviewFinder):
  def __init__(self, movie_name, imdb_url=None):
    super(MetacriticReviewFinder, self).__init__(movie_name, imdb_url, 'metacritic')

  def _FindReviewUrl(self, url_data, html=True):
    if html:
      regex = r'(http://www.metacritic.com/.*?)"'
    else:
      regex = r'(http://www.metacritic.com/.*)'
    review_urls = self._FindRegex(regex, url_data)
    if review_urls:
      return review_urls[0]
    return None

class RottenReviewFinder(ReviewFinder):
  def __init__(self, movie_name, imdb_url=None):
    super(RottenReviewFinder, self).__init__(movie_name, imdb_url, 'rotten tomatoes')

  def _FindReviewUrl(self, url_data, html=True):
    if html:
      regex = r'(http://www.rottentomatoes.com/.*?)"'
    else:
      regex = r'(http://www.rottentomatoes.com/m/.*)'
    review_urls = self._FindRegex(regex, url_data)
    # If there is link of format 'rottentomatoes.com/m/', always use it
    candidates = [x for x in review_urls if 'rottentomatoes.com/m/' in x]
    if candidates:
      review_urls = candidates
    for review in review_urls:
      if 'author' in review:
        continue
      if '/tools/' in review:
        continue
      return review
      break
    return None


def UpdateMovieReviews(days_back, search_web):
  imdb_access = imdb.IMDb()
  if not days_back:
    movies = Movie.objects.all()
  else:
    cutoff = datetime.datetime.now() - datetime.timedelta(days_back)
    movies = Movie.objects.filter(date_added__gt=cutoff)
  for movie in movies:
    if not movie.imdb_url or (movie.metacritic_url and movie.rotten_url):
      continue

    if not movie.metacritic_url:
      review_url = MetacriticReviewFinder(movie.name, movie.imdb_url).GetReviewUrl(search_web=search_web)
      if review_url:
        movie.metacritic_url = review_url
        movie.save()
        print '%s %s' % (movie, movie.metacritic_url)

    if not movie.rotten_url:
      review_url = RottenReviewFinder(movie.name, movie.imdb_url).GetReviewUrl(search_web=search_web)
      if review_url:
        movie.rotten_url = review_url
        movie.save()
        print '%s %s' % (movie, movie.rotten_url)


if __name__ == '__main__':
  usage = "usage: %prog [options]"
  parser = OptionParser(usage)
  parser.add_option('-d', '--days_back', type='int', dest='days_back', default=5,
                    help='skip movies older than this many days')
  parser.add_option('-s', '--search_web', action='store_true', default=False,
                    dest='search_web', help='search web for reviews')
  (options, args) = parser.parse_args()
  UpdateMovieReviews(days_back=options.days_back, search_web=options.search_web)
